
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "_Math.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////// 
 
#include "StateMachine.h"

StateMachine::StateMachine():
	m_sm_depth(-1),
	m_sm_delayedCommandsSize(0),
	m_bIsUpdatingState(false)
{
	#if DEBUG
		m_isStackModified = false;
	#endif
}

StateMachine::~StateMachine()
{
	
}

void StateMachine::InitStateMachine()
{

	m_sm_depth = -1;
	m_sm_delayedCommandsSize = 0;

	for ( int i = 0 ; i < STATES_MACHINE_MAX_DEPTH ; i++ )
	{
		m_sm_stack[i] = -1 ;
	}

	#if DEBUG
		m_isStackModified = false;
	#endif
}

void StateMachine::FreeStateMachine()
{
	for (int i = m_sm_depth; i >= 0; i--)
	{
		if(HasFocus(m_sm_stack[i]))
		{
			UpdateState(i, StateMachineMode::LostFocus);
		}
		UpdateState(i, StateMachineMode::Destructor);
	}

	m_sm_depth = -1;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

void StateMachine::PushState(int stateToPush, bool bImmediatMode)

{
	if(!m_bIsUpdatingState && bImmediatMode && (m_sm_delayedCommandsSize == 0))
	{
		ASSERT(m_sm_delayedCommandsSize == 0);
		Push(stateToPush);
	} else {
		m_sm_delayedCommands[m_sm_delayedCommandsSize] = PUSH_STATE;
		ASSERT(m_sm_delayedCommandsSize < STATES_MACHINE_MAX_DEPTH);
		m_sm_delayedCommandsParams[m_sm_delayedCommandsSize++] = stateToPush;
		ASSERT(m_sm_delayedCommandsSize < STATES_MACHINE_MAX_DEPTH);
	}
}



void StateMachine::Push(int stateToPush)
{
	if(m_sm_depth >= 0 && CurrentStateHasFocus() )
	{
		RemoveFocusFromCurrentState();
	}
	ASSERT((m_sm_depth + 1) < STATES_MACHINE_MAX_DEPTH);
	m_sm_stack[++m_sm_depth] = stateToPush;

	// Set the next state params position now
	//m_sm_params_positions[m_sm_depth + 1] = m_sm_params_current_position;

	UpdateState(m_sm_depth, StateMachineMode::Constructor);

	#if DEBUG
		m_isStackModified = true;
	#endif
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

void StateMachine::PopState(bool bImmediatMode)
{
	if(!m_bIsUpdatingState && bImmediatMode && (m_sm_delayedCommandsSize == 0))
	{
		ASSERT(m_sm_delayedCommandsSize == 0);
		Pop();
		/*if (CurrentStateHasFocus())
		{
			RemoveFocusFromCurrentState();
			UpdateState(m_sm_depth, StateMachineMode::LostFocus);
		}

		UpdateState(m_sm_depth, StateMachineMode::Destructor);

		ASSERT(m_sm_depth >= 0);
		m_sm_stack[m_sm_depth--] = -1;
		//m_sm_params_current_position = m_sm_params_positions[m_sm_depth + 1];
		#if DEBUG
			m_isStackModified = true;
		#endif*/
	} else {
		ASSERT(m_sm_delayedCommandsSize < STATES_MACHINE_MAX_DEPTH);
		m_sm_delayedCommands[m_sm_delayedCommandsSize++] = POP_STATE;
		ASSERT(m_sm_delayedCommandsSize < STATES_MACHINE_MAX_DEPTH);
	}
}

void StateMachine::Pop()
{
	if (CurrentStateHasFocus())
	{
		RemoveFocusFromCurrentState();
	}

	UpdateState(m_sm_depth, StateMachineMode::Destructor);

	ASSERT(m_sm_depth >= 0);
	m_sm_stack[m_sm_depth--] = -1;

	ASSERT(m_sm_depth >= -1);
	//m_sm_params_current_position = m_sm_params_positions[m_sm_depth + 1];
	#if DEBUG
		m_isStackModified = true;
	#endif
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------


void StateMachine::SwitchState(int stateToSwitchTo, bool bImmediatMode, bool popAllStates)
{
	//ASSERT( !popAllStates ); // implementation bugged, must be fixed before use
	ASSERT(m_sm_delayedCommandsSize == 0);

	if (popAllStates)
	{
		for (int i = m_sm_depth; i >= 0; i--)
		{
			PopState(bImmediatMode);
		}
	} 
	else 
	{
		PopState(bImmediatMode);
	}

	PushState(stateToSwitchTo, bImmediatMode);
}


//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

void StateMachine::PopUntil(int stateIndex, bool bImmediatMode)
{
	if(m_sm_delayedCommandsSize != 0)
	{
		m_sm_delayedCommands[m_sm_delayedCommandsSize] = POP_UNTIL;
		m_sm_delayedCommandsParams[m_sm_delayedCommandsSize] = stateIndex;
	}
	else
	{
		if(!m_bIsUpdatingState && bImmediatMode && (m_sm_delayedCommandsSize == 0))
		{
			while(m_sm_depth > 0 && CurrentState() != stateIndex)
			{
				PopState(bImmediatMode);
			}
		} else {
			int i = m_sm_depth;
			while(i > 0 && GetStateAtDepth(i) != stateIndex)
			{
				i--;
				PopState(bImmediatMode);
			}
		}
	}
}

void StateMachine::PopUntilEx(int stateIndex)
{		
	while(m_sm_depth > 0 && CurrentState() != stateIndex)
	{
		Pop();
	}
}


#define ISINFOCUS() HasFocus(m_sm_stack[depth])


//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
void StateMachine::UpdateState (int depth, int mode)
{
	m_bIsUpdatingState = true;
	if (mode == StateMachineMode::Draw2D  && !CurrentStateHasFocus() )
	{
		// States that do not have focus must be explicitly drawn by
		// a child state calling DispatchState(depth-1, Game::Draw)
		m_bIsUpdatingState = false;
		return;
	}
	else if (mode == (StateMachineMode::GainFocus))
	{
		//if(m_isStackModified)
		//{
		//	DEBUG_OUT("StateStack : ");
		//	PrintStateStack(3);
		//	m_isStackModified = false;
		//}
	}
	else if (mode == (StateMachineMode::Constructor))
	{
		// reset timer	
	}
	else if (mode == (StateMachineMode::Update))
	{
		do
		{
			if(m_sm_delayedCommandsSize)
			{
				for(int it = 0; it < m_sm_delayedCommandsSize; it++)
				{
					switch(m_sm_delayedCommands[it])
					{
					case POP_STATE:
						Pop();
						break;
					case PUSH_STATE:
						Push(m_sm_delayedCommandsParams[it]);
						break;
					case POP_UNTIL:
						PopUntilEx(m_sm_delayedCommandsParams[it]);
						break;
					}
				}
				m_sm_delayedCommandsSize = 0;
				depth = m_sm_depth;
			}

			if (!CurrentStateHasFocus())
			{
				GiveFocusToCurrentState();
			} 

		} while(m_sm_delayedCommandsSize);
	}

	ASSERT(depth >= 0);
	DispatchState(depth,mode);

	if (mode == (StateMachineMode::Update))
	{
		do
		{
			if(m_sm_delayedCommandsSize)
			{
				ASSERT(m_sm_delayedCommandsSize < STATES_MACHINE_MAX_DEPTH);
				for(int it = 0; it < m_sm_delayedCommandsSize; it++)
				{
					switch(m_sm_delayedCommands[it])
					{
					case POP_STATE:
						Pop();
						break;
					case PUSH_STATE:
						Push(m_sm_delayedCommandsParams[it]);
						break;
					case POP_UNTIL:
						PopUntilEx(m_sm_delayedCommandsParams[it]);
						break;
					}
				}
				m_sm_delayedCommandsSize = 0;
				depth = m_sm_depth;
				ASSERT(depth >= 0);
			}

			if (!CurrentStateHasFocus())
			{
				GiveFocusToCurrentState();
			} 
		} while(m_sm_delayedCommandsSize);		
	} 

	ASSERT(depth >= 0);
	m_bIsUpdatingState = false;
}


void StateMachine::PrintStateStack(int depths) 
{
	DEBUG_OUT("[");

	for(int i = 0; i < CMath::Min(depths, m_sm_depth); i++)
	{
		DEBUG_OUT(" ");
		TraceStateName( m_sm_stack[m_sm_depth - i] );
		//DEBUG_OUT(" %s", State_names[m_sm_stack[m_sm_depth - i]]);
	}

	if(depths < m_sm_depth)
	{
		DEBUG_OUT(" ...");
	}	

	DEBUG_OUT(" ]\n");
}


//-------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
